package com.qiaofang.applet.service.other.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.util.TypeUtils;
import com.google.common.collect.Lists;
import com.qiaofang.applet.common.base.MongoBaseService;
import com.qiaofang.applet.common.context.QfContextHolder;
import com.qiaofang.applet.common.context.ThreadLocalQfContextProvider;
import com.qiaofang.applet.common.exception.BusinessException;
import com.qiaofang.applet.common.util.RedisKeyUtil;
import com.qiaofang.applet.common.util.RedisUtil;
import com.qiaofang.applet.constants.BusinessConstants;
import com.qiaofang.applet.constants.DictConstants;
import com.qiaofang.applet.service.other.domain.DictCorp;
import com.qiaofang.applet.service.other.repository.DictCorpRepository;
import com.qiaofang.applet.service.other.service.DictCorpService;
import com.qiaofang.applet.service.other.service.dto.DictDifferentDto;
import com.qiaofang.applet.service.other.service.dto.DictDto;
import com.qiaofang.applet.service.other.service.mapstruct.DictCorpMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.ExecutableUpdateOperation;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author daiyuxiang
 */
@Slf4j
@Service
public class DictCorpServiceImpl extends MongoBaseService<DictCorp, String> implements DictCorpService {

    private static final Logger LOGGER = LoggerFactory.getLogger(DictCorpServiceImpl.class);

    private final DictCorpRepository dictCorpRepository;

    @Autowired
    private DictCorpMapper dictCorpMapper;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private ThreadLocalQfContextProvider threadLocalQfContextProvider;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired
    public DictCorpServiceImpl(DictCorpRepository dictCorpRepository) {
        super(dictCorpRepository);
        this.dictCorpRepository = dictCorpRepository;
    }

    /**
     * 初始化集团字典数据
     *
     * @param corporationCode
     */
    @Override
    public void initCorporationData(String corporationCode) {
        ClassPathResource resource = new ClassPathResource("corporation/init.json");
        InputStream is = null;
        BufferedReader br = null;
        StringBuilder sb = new StringBuilder();
        try {
            is = resource.getInputStream();
            br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            String inputStr;
            while ((inputStr = br.readLine()) != null) {
                sb.append(inputStr);
            }
        } catch (IOException e) {
            LOGGER.error("获取集团初始化数据文件失败", e);
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                LOGGER.error("获取集团初始化数据文件失败", e);
            }
            try {
                br.close();
            } catch (IOException e) {
                LOGGER.error("获取集团初始化数据文件失败", e);
            }
        }
        try {
            JSONArray jsonArray = JSONArray.parseArray(sb.toString());
            List<DictCorp> list = new ArrayList(jsonArray.size());
            ParserConfig config = ParserConfig.getGlobalInstance();
            for (Object o : jsonArray) {
                DictCorp dictCorp = TypeUtils.cast(o, DictCorp.class, config);
                dictCorp.setCorporationCode(corporationCode);
                list.add(dictCorp);
            }
            dictCorpRepository.saveAll(list);
        } catch (Exception e) {
            LOGGER.error("集团初始化数据文件转换异常，数据格式错误", e);
        }
    }

    @Override
    public void initCorporationDataByDictType(String corporationCode, String dictType) {
        ClassPathResource resource = new ClassPathResource("corporation/init.json");
        InputStream is = null;
        BufferedReader br = null;
        StringBuilder sb = new StringBuilder();
        try {
            is = resource.getInputStream();
            br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            String inputStr;
            while ((inputStr = br.readLine()) != null) {
                sb.append(inputStr);
            }
        } catch (IOException e) {
            LOGGER.error("获取集团初始化数据文件失败", e);
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                LOGGER.error("获取集团初始化数据文件失败", e);
            }
            try {
                br.close();
            } catch (IOException e) {
                LOGGER.error("获取集团初始化数据文件失败", e);
            }
        }
        try {
            JSONArray jsonArray = JSONArray.parseArray(sb.toString());
            List<DictCorp> list = new ArrayList(jsonArray.size());
            ParserConfig config = ParserConfig.getGlobalInstance();
            for (Object o : jsonArray) {
                DictCorp dictCorp = TypeUtils.cast(o, DictCorp.class, config);
                if (StringUtils.equals(dictCorp.getDictType(), dictType)) {
                    dictCorp.setCorporationCode(corporationCode);
                    list.add(dictCorp);
                }
            }
            dictCorpRepository.saveAll(list);
        } catch (Exception e) {
            LOGGER.error("集团初始化数据文件转换异常，数据格式错误", e);
        }
    }

    @Override
    public void removeCorporationData(String corporationCode) {
        dictCorpRepository.deleteAllByCorporationCode(corporationCode);
        redisUtil.del(RedisKeyUtil.getDictCacheKey(corporationCode));
    }

    @Override
    public void removeCorporationDataByDictType(String corporationCode, String dictType) {
        dictCorpRepository.deleteAllByCorporationCodeAndDictType(corporationCode, dictType);
        redisUtil.hdel(RedisKeyUtil.getDictCacheKey(corporationCode),dictType);
    }

    @Override
    public void saveDictCorp(List<DictCorp> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        for (DictCorp dictCorp : list) {
            saveDictCorp(dictCorp);
        }
    }

    @Override
    public void saveDictCorp(DictCorp dictCorp) {
        String id = dictCorp.getId();
        if (StringUtils.isEmpty(id)) {
            dictCorpRepository.insert(dictCorp);
        } else {
            updateById(dictCorp);
        }
        redisUtil.hdel(RedisKeyUtil.getDictCacheKey(dictCorp.getCorporationCode()), dictCorp.getDictType());
    }

    @Override
    public List<DictDto> getDictByCorporationCode(String corporationCode) {
        String dictCacheKey = RedisKeyUtil.getDictCacheKey(corporationCode);
        List<DictDto> list = new LinkedList<>();
        Object obj = redisUtil.hget(dictCacheKey, DictConstants.SLIDE_SHOW.getType());
        if (obj != null) {
            list.addAll(transBean2List(obj));
        } else {
            Query query = new Query().addCriteria(Criteria.where("corporationCode").is(corporationCode));
            query.addCriteria(Criteria.where("dictType").is(DictConstants.SLIDE_SHOW.getType()));
            query.with(Sort.by(Sort.Order.asc("dictSort")));
            list.addAll(dictCorpMapper.toDto(mongoTemplate.find(query, DictCorp.class)));
            redisUtil.hset(dictCacheKey, DictConstants.SLIDE_SHOW.getType(), list);
        }
        obj = redisUtil.hget(dictCacheKey, DictConstants.BOX_UTIL.getType());
        if (obj != null) {
            list.addAll(transBean2List(obj));
        } else {
            Query query = new Query().addCriteria(Criteria.where("corporationCode").is(corporationCode));
            query.addCriteria(Criteria.where("dictType").is(DictConstants.BOX_UTIL.getType()));
            query.with(Sort.by(Sort.Order.asc("dictSort")));
            list.addAll(dictCorpMapper.toDto(mongoTemplate.find(query, DictCorp.class)));
            redisUtil.hset(dictCacheKey, DictConstants.BOX_UTIL.getType(), list);
        }
        return list;
    }

    @Override
    public Map<String, List<DictDto>> getDictCollectionByType(Set<String> dictTypes, String corporationCode) {
        if (CollectionUtils.isEmpty(dictTypes)) {
            return null;
        }
        String corporationKey = RedisKeyUtil.getDictCacheKey(corporationCode);
        Map<String, List<DictDto>> map = new HashMap<>(dictTypes.size());
        for (String dictType : dictTypes) {
            Object obj = redisUtil.hget(corporationKey, dictType);
            if (obj != null) {
                map.put(dictType, transBean2List(obj));
                continue;
            }
            Query query = new Query().addCriteria(Criteria.where("corporationCode").is(corporationCode));
            query.addCriteria(Criteria.where("dictType").is(dictType));
            query.with(Sort.by(Sort.Order.asc("dictSort")));
            List<DictDto> list = dictCorpMapper.toDto(mongoTemplate.find(query, DictCorp.class));
            if (!CollectionUtils.isEmpty(list)) {
                map.put(dictType, list);
                redisUtil.hset(corporationKey, dictType, list);
            }
        }
        return map;
    }

    @Override
    public List<DictDto> getToolList() {
        String corporationCode = QfContextHolder.getContext().getCorporationCode();
        Query query = new Query().addCriteria(Criteria.where("corporationCode").is(corporationCode));
        query.addCriteria(Criteria.where("dictType").is(DictConstants.BOX_UTIL.getType()));
        query.addCriteria(Criteria.where("dictStatus").is(1));
        query.with(Sort.by(Sort.Order.asc("dictSort")));
        return dictCorpMapper.toDto(mongoTemplate.find(query, DictCorp.class));
    }

    @Override
    public List<DictDto> getSlideShow() {
        String corporationCode = QfContextHolder.getContext().getCorporationCode();
        String corporationKey = RedisKeyUtil.getDictCacheKey(corporationCode);
        Object obj = redisUtil.hget(corporationKey, DictConstants.SLIDE_SHOW.getType());
        List<DictDto> list = null;
        if (obj != null) {
            list = transBean2List(obj);
        } else {
            Query query = new Query().addCriteria(Criteria.where("corporationCode").is(corporationCode));
            query.addCriteria(Criteria.where("dictType").is(DictConstants.SLIDE_SHOW.getType()));
            query.with(Sort.by(Sort.Order.asc("dictSort")));
            list = dictCorpMapper.toDto(mongoTemplate.find(query, DictCorp.class));
            redisUtil.hset(corporationKey, DictConstants.SLIDE_SHOW.getType(), list);
        }
        return list;
    }

    @Override
    public Map<String, List<DictDto>> getDictCollectionByPropertyType(String propertyType, String corporationCode) {
        if (propertyType == null) {
            return null;
        }
        //新房
        Set<String> dictTypes = null;
        if (StringUtils.equals(propertyType, BusinessConstants.FIRSTHAND_ESTATE.getValue())) {
            String strArr[] = {DictConstants.QF_PROPERTY_TYPE.getType(), DictConstants.PROPERTY_AREA.getType(), DictConstants.ORIENTATION_TYPE.getType(),
                    DictConstants.AVERAGE_PRICE.getType(), DictConstants.SALE_STATUS.getType(), DictConstants.HOUSE_TYPE.getType()
                    , DictConstants.BUILDING_TYPE.getType()};
            dictTypes = new HashSet<>(Arrays.asList(strArr));
        }
        //二手房
        if (StringUtils.equals(propertyType, BusinessConstants.SALE_PROPERTY.getValue())) {
            String strArr[] = {DictConstants.PROPERTY_AREA.getType(), DictConstants.DECORATION_SITUATION_TYPE.getType(), DictConstants.SELL_PRICE.getType(),
                    DictConstants.ORIENTATION_TYPE.getType(), DictConstants.LIFT_TYPE.getType(), DictConstants.HOUSE_TYPE.getType()
                    , DictConstants.UNIT_PRICE.getType(), DictConstants.FLOOR_TYPE.getType(), DictConstants.IS_VR.getType()};
            dictTypes = new HashSet<>(Arrays.asList(strArr));
        }
        //租房
        if (StringUtils.equals(propertyType, BusinessConstants.RENT_PROPERTY.getValue())) {
            String strArr[] = {DictConstants.PROPERTY_AREA.getType(), DictConstants.RENT_AMOUNT.getType(), DictConstants.DECORATION_SITUATION_TYPE.getType(),
                    DictConstants.SELL_PRICE.getType(), DictConstants.ORIENTATION_TYPE.getType(), DictConstants.LIFT_TYPE.getType()
                    , DictConstants.HOUSE_TYPE.getType(), DictConstants.FLOOR_TYPE.getType(), DictConstants.IS_VR.getType()};
            dictTypes = new HashSet<>(Arrays.asList(strArr));
        }
        return getDictCollectionByType(dictTypes, corporationCode);
    }

    private Map<String, List<DictDto>> transBean2Map(Object obj) {
        Map<String, List<DictDto>> mapResult = new HashMap<>();
        String objString = JSONObject.toJSONString(obj);
        Map map = JSONObject.parseObject(objString, Map.class);

        map.forEach((k, v) -> {
            List<DictDto> dictDtos = JSONObject.parseArray(JSONObject.toJSONString(v), DictDto.class);
            mapResult.put(k.toString(), dictDtos);
        });
        return mapResult;
    }

    private List<DictDto> transBean2List(Object obj) {
        return JSONObject.parseArray(JSONObject.toJSONString(obj), DictDto.class);
    }

    @Override
    public void initConstanceData(String corporationCode) {
        String corporationKey = RedisKeyUtil.getDictCacheKey(corporationCode);
        Query query = new Query().addCriteria(Criteria.where("corporationCode").is(corporationCode));
        query.with(Sort.by(Sort.Order.asc("dictType"), Sort.Order.asc("dictSort")));
        try {
            Map<String, List<DictDto>> resultMap = Optional.ofNullable(dictCorpMapper.toDto(mongoTemplate.find(query, DictCorp.class))).orElseGet(Lists::newArrayList)
                    .stream().collect(Collectors.groupingBy(s -> s.getDictType()));
            redisTemplate.opsForHash().putAll(corporationKey, resultMap);
        } catch (Exception e) {
            log.error("initConstantdata error:{}", JSON.toJSONString(e));
        }
    }

    @Override
    public Map<String, List<DictDto>> getDictCollectionByCorporationCode(String corporationCode) {
        String corporationKey = RedisKeyUtil.getDictCacheKey(corporationCode);
        Query query = new Query().addCriteria(Criteria.where("corporationCode").is(corporationCode));

        String dictTypes[] = {DictConstants.SUBJECT_COLOR.getType(), DictConstants.INDEX_TOP_SHOW.getType(), DictConstants.SLIDE_SHOW.getType(),
                DictConstants.BOX_UTIL.getType(), DictConstants.FIXED_TOPIC_NAME.getType(), DictConstants.FY_RECOMMEND_SHOW.getType()
                ,DictConstants.INDEX_PAGE.getType()};
        //需要查询数据库的字典类型
        Set<String> dictTypeQureys = new HashSet<>();
        Map<String, List<DictDto>> cacheDataMap = new HashMap<>();
        for (String dictType : dictTypes) {
            Object hget = redisUtil.hget(corporationKey, dictType);
            if (hget == null) {
                dictTypeQureys.add(dictType);
            } else {
                List<DictDto> dictDtos = transBean2List(hget);
                cacheDataMap.put(dictType,dictDtos);
            }
        }
        query.addCriteria(Criteria.where("dictType").in(dictTypeQureys));
        query.with(Sort.by(Sort.Order.asc("dictType"), Sort.Order.asc("dictSort")));
        //当前集团下的字典
        Map<String, List<DictDto>> queryDataMap = Optional.ofNullable(dictCorpMapper.toDto(mongoTemplate.find(query, DictCorp.class))).orElseGet(Lists::newArrayList)
                .stream().collect(Collectors.groupingBy(s -> s.getDictType()));
        for (String dictTypeQurey : dictTypeQureys) {
            redisUtil.hset(corporationKey, dictTypeQurey, queryDataMap.get(dictTypeQurey));
        }
        Map<String, List<DictDto>> resultMap = new HashMap<>(cacheDataMap.size() + queryDataMap.size());
        resultMap.putAll(cacheDataMap);
        resultMap.putAll(queryDataMap);
        return resultMap;
    }

    @Override
    public List<DictDto> getOtherLogo(String corporationCode) {
        String corporationKey = RedisKeyUtil.getDictCacheKey(corporationCode);
        Object obj = redisUtil.hget(corporationKey, DictConstants.OTHER_LOGO.getType());
        List<DictDto> list = null;
        if (obj != null) {
            list = transBean2List(obj);
        } else {
            Query query = new Query().addCriteria(Criteria.where("corporationCode").is(corporationCode));
            query.addCriteria(Criteria.where("dictType").is(DictConstants.OTHER_LOGO.getType()));
            query.with(Sort.by(Sort.Order.asc("dictSort")));
            list = dictCorpMapper.toDto(mongoTemplate.find(query, DictCorp.class));
            redisUtil.hset(corporationKey, DictConstants.OTHER_LOGO.getType(), list);
        }
        return list;
    }

    @Override
    public void incrementAddToDict(String corporationCode, List<DictCorp> initList) {
        try {
            // 查询集团下目前已有的字典数据
            List<DictDifferentDto> oldList = Optional.ofNullable(dictCorpRepository.findAllByCorporationCode(corporationCode))
                    .orElseGet(Lists::newArrayList).stream().map(dictCorpMapper::domainToDto).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(oldList)) {
                log.info("corporationCode {}:", corporationCode);
                throw new BusinessException("操作错误,该集团未进行初始化操作:" + corporationCode);
            }

            List<DictDifferentDto> newList = Optional.ofNullable(initList).orElseGet(Lists::newArrayList)
                    .stream().map(dictCorpMapper::domainToDto).collect(Collectors.toList());

            newList.removeAll(oldList);
            log.info("corporationCode : {}.增量字典数据 :{}", corporationCode, JSONObject.toJSONString(newList));
            if(CollectionUtils.isEmpty(newList)) {
                return;
            }

            DictCorp dictCorp;
            List<DictCorp> newDictCorpList = new ArrayList<>();
            for (DictDifferentDto differentDto : newList) {
                Optional<DictCorp> optional = initList.stream().filter(f -> StringUtils.equals(differentDto.getDictCode(), f.getDictCode())).findFirst();
                if (optional.isPresent()) {
                    dictCorp = optional.get();
                    dictCorp.setId(null);
                    dictCorp.setCorporationCode(corporationCode);
                    newDictCorpList.add(dictCorp);
                }
            }
            dictCorpRepository.saveAll(newDictCorpList);
        } catch (Exception e) {
            LOGGER.error("批量添加集团增量字典数据异常：{}", e);
        }
    }
}
